-
Notifications
You must be signed in to change notification settings - Fork 2.7k
[Light] synchronize cht pruning with babe pruning #6851
Conversation
client/api/src/backend.rs
Outdated
| #[derive(Clone)] | ||
| /// Pruning requirement to share between multiple client component. | ||
| /// | ||
| /// This allows pruning related synchronisation. For instance in light | ||
| /// client we need to synchronize header pruning from CHT (every N blocks) | ||
| /// with the pruning from consensus used (babe for instance require that | ||
| /// its epoch headers are not pruned which works as long as the slot length | ||
| /// is less than the CHT pruning window. | ||
| /// Each compenent register at a given index (call are done by this order). | ||
| /// | ||
| /// Note that this struct could be split in two different struct (provider without | ||
| /// component and Component), depending on future usage of this shared info. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| #[derive(Clone)] | |
| /// Pruning requirement to share between multiple client component. | |
| /// | |
| /// This allows pruning related synchronisation. For instance in light | |
| /// client we need to synchronize header pruning from CHT (every N blocks) | |
| /// with the pruning from consensus used (babe for instance require that | |
| /// its epoch headers are not pruned which works as long as the slot length | |
| /// is less than the CHT pruning window. | |
| /// Each compenent register at a given index (call are done by this order). | |
| /// | |
| /// Note that this struct could be split in two different struct (provider without | |
| /// component and Component), depending on future usage of this shared info. | |
| /// Pruning requirement to share between multiple client component. | |
| /// | |
| /// This allows pruning related synchronisation. For instance in light | |
| /// client we need to synchronize header pruning from CHT (every N blocks) | |
| /// with the pruning from consensus used (babe for instance require that | |
| /// its epoch headers are not pruned which works as long as the slot length | |
| /// is less than the CHT pruning window. | |
| /// Each compenent register at a given index (call are done by this order). | |
| /// | |
| /// Note that this struct could be split in two different struct (provider without | |
| /// component and Component), depending on future usage of this shared info. | |
| #[derive(Clone)] |
client/api/src/backend.rs
Outdated
| #[derive(Eq, PartialEq)] | ||
| /// Define a block number limit to apply. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| #[derive(Eq, PartialEq)] | |
| /// Define a block number limit to apply. | |
| /// Define a block number limit to apply. | |
| #[derive(Eq, PartialEq)] |
too but would need to use traits). Removed checked optimization.
|
I did simplify code a bit, I can still use traits to make instantiation a bit more safer, I could also drop support for other source of constraint and have a non generic code. |
client/api/src/backend.rs
Outdated
| pub enum PruningLimit<N> { | ||
| /// Ignore. | ||
| None, | ||
| /// The component require at least this number |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Iiuc from the code, N here is actually number of oldest block that shouldn't be pruned. If I'm right, then the comment looks wrong - i.e. some component may need last 1024 headers, but we may be at block 1_000_000 => N would actually be 1_000_000 - 1024
client/db/src/light.rs
Outdated
| cache: Arc<DbCacheSync<Block>>, | ||
| header_metadata_cache: Arc<HeaderMetadataCache<Block>>, | ||
| shared_pruning_requirements: SharedPruningRequirementsSource<Block>, | ||
| pending_cht_pruning: RwLock<VecDeque<(NumberFor<Block>, NumberFor<Block>)>>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually we may only store number of the oldest+newest block we want to prune. I.e. instead of vec![(1, 2048), (2049, 4096), (4097, 5120), ..., (5121, 8192)] we may only store two numbers - 1 and 8192 :)
|
Re simpler option - I haven't finished reviewing yet + I only have shallow knowledge of epochs + pruning. But would it be enough to know if header One last addition - iiuc the BABE fails to import block if pruning fails, right? How critical is pruning for BABE? If it isn't critical, then probably it is better just to log the issue, not to fail the process? |
I wonder if knowing if H is canonical/non-canonical is actually the operation in fork tree that require a to read cht pruned headers (basically it fails when looking for common ancestor of two epoch fork tree roots). substrate/utils/fork-tree/src/lib.rs Line 147 in 488b7c7
I am not sure I understand this correctly, but yes I feel like there may be something to do in babe to avoid querying old headers, but my attempts at doing so were not very successful.
Seems like a good idea to me. |
This reverts commit bf40274. Allowing failure on a PR that try to avoid failure seems awkward.
|
My idea is to prune all fork tree nodes that correspond to blocks with number < best_finalized_number and that are non-canonical (this is why we need to leave So basically you start with roots - if some roots have number < last_finalized and they're non-canonical (doesn't require header in storage), then you prune it + its children. Then you proceed to selected root children && only leave canonical again. Continue until you'll find that all roots must be kept. Does it makes sense? |
|
I see, keep a KeyLookup mapping to resolve the fork tree pruning (remove non-canonical roots without having to calculate a common parent through headers). So in this case If keeping shared memory we could also simply store those fork tree roots in the SharedPruningRequirement and calculate their new state before cht pruning. But that is only too optimize the db footprint and is probably not worth it. I will try the first solution. |
Probably better to leave this info in light-db forever - it'll be easier (unless it breaks anything else). Maybe optimization for follow-up PRs? Not sure? But I'm not insisting :) |
something less technical.
This reverts commit e9255a2.
|
seems like synch it is working ok with new code (the number of key lookup sometime peeks a lot but seems to get down close to the number of headers sometimes. |
|
Closed due to it being stale for while. |
Light client Cht pruning on polkadot is erasing headers that babe requires during its epoch pruning.
The header cache allows thing to work, except if the node get shutdown and restarted, then the db misses some header and the chain get bricked.
This PR create a
SharedPruningRequirementsstruct that is designed to be passed around client component and to contain db pruning relative constraints.Then the CHT pruning (when babe set
need_mapping_for_light_pruningof the shared pruning requirement to true) will notprune the number to key lookup for the canonical header.
That is babe that later will prune it through its fork tree epoch pruning.
For this PR the only constraint is a finalized block height to keep untouched, it is set by babe depending on its stored epochs and locks the cht pruning of the light client headers.
This means that in babe we update this info after epoch pruning, and in the light client limit cht pruning and when limit applies we need to buffered the pending pruning ranges.
Note that someone with better grasp of ForkTree and babe EpochChange may be able to do thin the
needed_parent_relationrange.At first I tried to do things in a simplier/quickier way, but none of my attempt where good, but there may be a more straight forward solution that do not rely on asumptions (would be better that this pr that adds code).
Failed previous attempts: